<!DOCTYPE HTML>
<html lang="zh-CN">
<head><meta name="generator" content="Hexo 3.9.0">
    <!--Setting-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta name="apple-mobile-web-app-capable" content="武威涛哥的博客">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">
    <meta name="browsermode" content="application">
    <meta name="screen-orientation" content="portrait">
    <meta name="theme-version" content="1.2.3">
    <meta name="root" content="/">
    <link rel="dns-prefetch" href="https://jaredtao.github.io">
    <!--SEO-->

    <meta name="keywords" content="Qt,Log,日志系统,轻量级Log系统">


    <meta name="description" content="
简介
预览
原理
html格式的log
Qt的log系统
融合


文件句柄复用
多线程测试
github仓库链接

简介一个完善的软件工程，自然是少不了log系统的。
这次涛哥教大家，用最少...">



<meta name="robots" content="all">
<meta name="google" content="all">
<meta name="googlebot" content="all">
<meta name="verify" content="all">

    <!--Title-->

<title>
    
    Qt自制简易好看的日志系统 |
    
    武威涛哥的博客
</title>

    <link rel="alternate" href="/atom.xml" title="武威涛哥的博客" type="application/atom+xml">


    <link rel="icon" href="/favicon.ico">

    



<link rel="stylesheet" href="/css/bootstrap.min.css?rev=3.3.7">
<link rel="stylesheet" href="/css/font-awesome.min.css?rev=4.7.0">
<link rel="stylesheet" href="/css/style.css?rev=@@hash">




    
	<div class="hide">
		<script type="text/javascript">
			var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan class='cnzz_stat_icon_1263868967 hide' %3E%3Cscript%20src%3D%22https%3A%2F%2Fs95.cnzz.com%2Fz_stat.php%3Fweb_id%3D1272564536%22%3E%3C%2Fscript%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s19.cnzz.com/z_stat.php%3Fid%3D1263868967%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));
		</script>
	</div>






    

    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https') {
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else {
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>

</head>

</html>
<!--[if lte IE 8]>
<style>
    html{ font-size: 1em }
</style>
<![endif]-->
<!--[if lte IE 9]>
<div style="ie">你使用的浏览器版本过低，为了你更好的阅读体验，请更新浏览器的版本或者使用其他现代浏览器，比如Chrome、Firefox、Safari等。</div>
<![endif]-->

<body>
    <header class="main-header"  style="background-image:url(http://snippet.shenliyang.com/img/banner.jpg)"  >
    <div class="main-header-box">
        <a class="header-avatar" href="/" title='JaredTao'>
            <img src="/img/avatar.jpg" alt="logo头像" class="img-responsive center-block">
        </a>
        <div class="branding">
        	<!--<h2 class="text-hide">Snippet主题,从未如此简单有趣</h2>-->
            
                <h2> 传承工匠精神 </h2>
            
    	</div>
    </div>
</header>
    <nav class="main-navigation">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="navbar-header"><span class="nav-toggle-button collapsed pull-right" data-toggle="collapse" data-target="#main-menu" id="mnav">
                    <span class="sr-only"></span>
                        <i class="fa fa-bars"></i>
                    </span>
                    <a class="navbar-brand" href="https://jaredtao.github.io">武威涛哥的博客</a>
                </div>
                <div class="collapse navbar-collapse" id="main-menu">
                    <ul class="menu">
                        
                            <li role="presentation" class="text-center">
                                <a href="/"><i class="fa "></i>首页</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/categories/Qml组件化编程/"><i class="fa "></i>Qml组件化编程</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/categories/Qml特效/"><i class="fa "></i>Qml特效</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/categories/Qt进阶之路/"><i class="fa "></i>Qt进阶之路</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/categories/C-修炼之路/"><i class="fa "></i>C++修炼之路</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/categories/武威故事/"><i class="fa "></i>武威故事</a>
                            </li>
                        
                            <li role="presentation" class="text-center">
                                <a href="/archives/"><i class="fa "></i>时间轴</a>
                            </li>
                        
                    </ul>
                </div>
            </div>
        </div>
    </div>
</nav>
    <section class="content-wrap">
        <div class="container">
            <div class="row">
                <main class="col-md-8 main-content m-post">
                    <p id="process"></p>
<article class="post">
    <div class="post-head">
        <h1 id="Qt自制简易好看的日志系统">
            
	            Qt自制简易好看的日志系统
            
        </h1>
        <div class="post-meta">
    
        <span class="categories-meta fa-wrap">
            <i class="fa fa-folder-open-o"></i>
            <a class="category-link" href="/categories/Qt进阶之路/">Qt进阶之路</a>
        </span>
    

    
        <span class="fa-wrap">
            <i class="fa fa-tags"></i>
            <span class="tags-meta">
                
                    <a class="tag-link" href="/tags/Log/">Log</a> <a class="tag-link" href="/tags/Qt/">Qt</a> <a class="tag-link" href="/tags/日志系统/">日志系统</a> <a class="tag-link" href="/tags/轻量级Log系统/">轻量级Log系统</a>
                
            </span>
        </span>
    

    
        
        <span class="fa-wrap">
            <i class="fa fa-clock-o"></i>
            <span class="date-meta">2019/04/30</span>
        </span>
        
            <span class="fa-wrap">
                <i class="fa fa-eye"></i>
                <span id="busuanzi_value_page_pv"></span>
            </span>
        
    
</div>
            
            
            <p class="fa fa-exclamation-triangle warning">
                本文于<strong>317</strong>天之前发表，文中内容可能已经过时。
            </p>
        
    </div>
    
    <div id="post-gallery">
        
          <img src="/img/avatar.jpg" alt="gallery-img" class="gallery">
        
    </div>

    <div class="post-body post-content">
        <ul>
<li><a href="#%E7%AE%80%E4%BB%8B">简介</a></li>
<li><a href="#%E9%A2%84%E8%A7%88">预览</a></li>
<li><a href="#%E5%8E%9F%E7%90%86">原理</a><ul>
<li><a href="#html%E6%A0%BC%E5%BC%8F%E7%9A%84log">html格式的log</a></li>
<li><a href="#qt%E7%9A%84log%E7%B3%BB%E7%BB%9F">Qt的log系统</a></li>
<li><a href="#%E8%9E%8D%E5%90%88">融合</a></li>
</ul>
</li>
<li><a href="#%E6%96%87%E4%BB%B6%E5%8F%A5%E6%9F%84%E5%A4%8D%E7%94%A8">文件句柄复用</a></li>
<li><a href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B5%8B%E8%AF%95">多线程测试</a></li>
<li><a href="#github%E4%BB%93%E5%BA%93%E9%93%BE%E6%8E%A5">github仓库链接</a></li>
</ul>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>一个完善的软件工程，自然是少不了log系统的。</p>
<p>这次涛哥教大家，用最少的代码做一个轻量又好看的log系统。</p>
<p>涛哥知道有现成的log4cpp、log4cplus之类的，也有使用过。</p>
<p>这次是抱着学习的心态来造这个轮子的，造轮子的过程才能学到</p>
<p>更多知识，才能有进步、有提升，难道不是么？</p>
<h2 id="预览"><a href="#预览" class="headerlink" title="预览"></a>预览</h2><p>先看一下成果</p>
<p><img src="/images/Logger/1.png" alt="预览"></p>
<h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><h3 id="html格式的log"><a href="#html格式的log" class="headerlink" title="html格式的log"></a>html格式的log</h3><p>为了实现 “代码最少” 和 “好看” 的需求，涛哥把log写进了一个html文件。</p>
<p>这样的log相当于一个静态的网页，只要装有浏览器的操作系统，都可以打开并看到上面图示那样的log。</p>
<p>涛哥给这个html文件设计了一个固定的模板的:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="utf-8" standalone="yes"?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>TaoLogger<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=utf-8"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span> <span class="attr">id</span>=<span class="string">"logCss"</span>&gt;</span></span><br><span class="line">        body &#123;</span><br><span class="line">            background: #18242b;</span><br><span class="line">            color: #afc6d1;</span><br><span class="line">            margin-right: 20px;</span><br><span class="line">            margin-left: 20px;</span><br><span class="line">            font-size: 14px;</span><br><span class="line">            font-family: Arial, sans-serif, sans;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        a &#123;</span><br><span class="line">            text-decoration: none;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        a:link &#123;</span><br><span class="line">            color: #a0b2bb;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        a:active &#123;</span><br><span class="line">            color: #f59504;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        a:visited &#123;</span><br><span class="line">            color: #adc7d4;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        a:hover &#123;</span><br><span class="line">            color: #e49115;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        h1 &#123;</span><br><span class="line">            text-align: center;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        h2 &#123;</span><br><span class="line">            color: #ebe5e5;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .d,</span><br><span class="line">        .w,</span><br><span class="line">        .c,</span><br><span class="line">        .f,</span><br><span class="line">        .i &#123;</span><br><span class="line">            padding: 3px;</span><br><span class="line">            overflow: auto;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .d &#123;</span><br><span class="line">            background-color: #0f1011;</span><br><span class="line">            color: #a8c1ce;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .i &#123;</span><br><span class="line">            background-color: #294453;</span><br><span class="line">            color: #a8c1ce;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .w &#123;</span><br><span class="line">            background-color: #7993a0;</span><br><span class="line">            color: #1b2329;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .c &#123;</span><br><span class="line">            background-color: #ff952b;</span><br><span class="line">            color: #1d2930;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        .f &#123;</span><br><span class="line">            background-color: #fc0808;</span><br><span class="line">            color: #19242b;</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"https://jaredtao.github.io"</span>&gt;</span>TaoLogger<span class="tag">&lt;/<span class="name">a</span>&gt;</span> 日志文件<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/JavaScript"</span>&gt;</span></span><br><span class="line">        function objHide(obj) &#123;</span><br><span class="line">            obj.style.display="none"</span><br><span class="line">        &#125;</span><br><span class="line">        function objShow(obj) &#123;</span><br><span class="line">            obj.style.display="block"</span><br><span class="line">        &#125;</span><br><span class="line">        function selectType() &#123;</span><br><span class="line">            var sel = document.getElementById("typeSelect");</span><br><span class="line">            const hideList = new Set(['d', 'i', 'w', 'c', 'f']);</span><br><span class="line">            if (sel.value === 'a') &#123;</span><br><span class="line">                hideList.forEach(element =&gt; &#123;</span><br><span class="line">                    var list = document.querySelectorAll('.' + element);</span><br><span class="line">                    list.forEach(objShow);</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                var ss = hideList;</span><br><span class="line">                ss.delete(sel.value);</span><br><span class="line">                ss.forEach(element =&gt; &#123;</span><br><span class="line">                    var list = document.querySelectorAll('.' + element);</span><br><span class="line">                    list.forEach(objHide);</span><br><span class="line">                &#125;);</span><br><span class="line">                var showList = document.querySelectorAll('.' + sel.value);</span><br><span class="line">                showList.forEach(objShow);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"typeSelect"</span> <span class="attr">onchange</span>=<span class="string">"selectType()"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'a'</span> <span class="attr">selected</span>=<span class="string">"selected"</span>&gt;</span>All<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'d'</span>&gt;</span>Debug<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'i'</span>&gt;</span>Info<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'w'</span>&gt;</span>Warning<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'c'</span>&gt;</span>Critical<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">'f'</span>&gt;</span>Fatal<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>（如果你不懂html，也没关系，直接拿过去用就好了）</p>
<p>这个模板只使用了一些很基本的html元素和css样式表，筛选器那里用了一点JavaScript。</p>
<p>(筛选器功能，我去请教了一下前端的同事，给了我一个JQuery版本，只要很少几行代码，但是要带上一个大大的JQuery.js。。。)</p>
<p>(涛哥我也写了不少qml，多多少少还是懂点js的，于是就自己写了这么一个筛选器。不到20行代码，真是自己动手丰衣足食啊。)</p>
<ul>
<li>Log模板的用法</li>
</ul>
<p>很简单的，模板作为html文件的前面部分，接下来每一行log，以追加的方式跟在模板后面就行了。</p>
<p>(html的body结束标记并没有写，浏览器都能正常打开。容错性真的强！)</p>
<p>当然, 每一条log有个格式要求:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"d"</span>&gt;</span> 山有木兮木有枝，心悦君兮君不知。<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>就是增加了一对div标记， div的class属性要设置为d、i、w、c、f这几个字符中的一个，分别是</p>
<p>debug、info、warning、critical、fatal的首字母, 这正是Qt所提供的log分类。</p>
<p>设置div的class属性，就是给筛选器用来做筛选。</p>
<ul>
<li>Log模板的存取</li>
</ul>
<p>文件读取? 不，太慢了。</p>
<p>这就是一段固定的字符串，直接编译进代码里，程序启动的时候直接装载到内存就好了。</p>
<p>那么C++里面，怎么才能装下这段带有转义字符的字符串呢？涛哥的答案是：C++11的 “原始字符串字面量”或者叫 “R字符串”</p>
<p>可以参考这里 <a href="https://zh.cppreference.com/w/cpp/language/string_literal" target="_blank" rel="noopener">cppreference</a></p>
<p>简单来说，是这样写的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">string</span> logTemplate = <span class="string">R"(xxxxxx)"</span>;</span><br></pre></td></tr></table></figure>

<p>只要有了 R”(  )” 这个写法，括号中间随便写转义字符、换行符都行。当然为了方便让编译器识别哪个</p>
<p>才是真正的’结束括号’，C++11标准提出了括号前后增加分隔符的写法，即:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">string</span> logTemplate = <span class="string">R"prefix(xxxxxx)prefix"</span>;</span><br></pre></td></tr></table></figure>

<p>左括号的前面和右括号的后面, 是同样的一段字符串作为分隔符就行了。</p>
<p>涛哥的代码里是这么用的</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> Logger</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">static</span> QString logTemplate = u8R<span class="string">"logTemplate(</span></span><br><span class="line"><span class="string">&lt;?xml version="</span><span class="number">1.0</span><span class="string">" encoding="</span>utf<span class="number">-8</span><span class="string">" standalone="</span>yes<span class="string">"?&gt;</span></span><br><span class="line"><span class="string">&lt;!DOCTYPE html PUBLIC "</span>-<span class="comment">//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span></span><br><span class="line">&lt;html&gt;</span><br><span class="line"></span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;title&gt;TaoLogger&lt;/title&gt;</span><br><span class="line">    ...</span><br><span class="line">    这里省略一大堆html代码</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    )logTemplate<span class="string">";</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="Qt的log系统"><a href="#Qt的log系统" class="headerlink" title="Qt的log系统"></a>Qt的log系统</h3><ul>
<li>Qt的log分类</li>
</ul>
<p>Qt的打印信息，大家普遍使用的是qDebug，不过Qt除了qDebug，还有qInfo, qWarning, qCritical等等。</p>
<p>涛哥翻了Qt5.12的源码，发现这几个打印最终都是通过fprintf(stderr)或者fprintf(stdout)来实现输出的，</p>
<p>不同的地方就在于Log类型。如果要用好这个分类，那我们平时使用打印的时候，就要注意做区分:</p>
<pre><code>- 调试信息用qDebug

- 常规信息用qInfo

- 警告用qWarning

- 比较严重的问题用qCritical</code></pre><ul>
<li>Qt的log格式化</li>
</ul>
<p>Qt提供了一个函数qSetMessagePattern，用来定制输出信息。</p>
<p>例如：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">qSetMessagePattern(<span class="string">"[%&#123;time yyyyMMdd h:mm:ss.zzz t&#125; %&#123;if-debug&#125;D%&#123;endif&#125;%&#123;if-info&#125;I%&#123;endif&#125;%&#123;if-warning&#125;W%&#123;endif&#125;%&#123;if-critical&#125;C%&#123;endif&#125;%&#123;if-fatal&#125;F%&#123;endif&#125;] %&#123;file&#125;:%&#123;line&#125; - %&#123;message&#125;"</span>);</span><br></pre></td></tr></table></figure>

<p>一般只要在main.cpp中添加这一行代码，之后的qDebug、qInfo等函数都会按照这个格式来输出，包含了</p>
<p>时间戳、log类型、文件名、行号 等信息。也可以不改任何代码、改环境变量来做到</p>
<p><img src="/images/Logger/4.png" alt="预览"><br><img src="/images/Logger/5.png" alt="预览"></p>
<ul>
<li>Release模式信息缺失</li>
</ul>
<p>这里有个问题，就是文件名和行号在debug模式正常，Release模式会变成空的。</p>
<p>要解决这个问题，那么就需要编译器提供的内置宏<code>__FILE__</code> 和 <code>__LINE__</code>了</p>
<p>涛哥写了这样几个宏，代替qDebug和qInfo等函数。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_DEBUG qDebug() &lt;&lt; __FILE__ &lt;&lt; __FUNCTION__ &lt;&lt; __LINE__</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_INFO qInfo() &lt;&lt; __FILE__ &lt;&lt; __FUNCTION__ &lt;&lt; __LINE__</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_WARN qWarning() &lt;&lt; __FILE__ &lt;&lt; __FUNCTION__ &lt;&lt; __LINE__</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_CRIT qCritical() &lt;&lt; __FILE__ &lt;&lt; __FUNCTION__ &lt;&lt; __LINE__</span></span><br></pre></td></tr></table></figure>

<p>用法类似这样:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LOG_DEBUG &lt;&lt; <span class="string">u8"山有木兮木有枝，心悦君兮君不知。"</span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>Qt的写log文件</li>
</ul>
<p>Qt还提供了一个函数 qInstallMessageHandler，可以插入一个回调函数，让每一行qDebug/qInfo等</p>
<p>函数的打印信息，都经过这个回调来处理。看一下帮助文档：</p>
<p><img src="/images/Logger/6.png" alt="预览"></p>
<p>其实帮助文档已经提供了一个简易的log功能，涛哥就是在这个功能的基础上，做了一些定制化的修改。</p>
<h3 id="融合"><a href="#融合" class="headerlink" title="融合"></a>融合</h3><p><img src="/images/Logger/3.png" alt="预览"></p>
<ul>
<li>log存储路径和容量</li>
</ul>
<p>涛哥写了一个函数和一组静态变量，用来设置和记录log存储的路径和容量</p>
<p>头文件中的声明</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;QDebug&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> Logger</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//默认存储路径为当前路径的Log文件夹下，默认文件数量为1024</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">initLog</span><span class="params">(<span class="keyword">const</span> QString&amp; logPath = QStringLiteral(<span class="string">"Log"</span>), <span class="keyword">int</span> logMaxCount = <span class="number">1024</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125; <span class="comment">// namespace Logger</span></span><br></pre></td></tr></table></figure>

<p>CPP中的实现</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> Logger</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//静态变量，记录存储路径</span></span><br><span class="line"><span class="keyword">static</span> QString gLogDir;</span><br><span class="line"><span class="comment">//静态变量，记录最大存储数量</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> gLogMaxCount;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">initLog</span><span class="params">(<span class="keyword">const</span> QString &amp;logPath, <span class="keyword">int</span> logMaxCount)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//安装回调</span></span><br><span class="line">    qInstallMessageHandler(outputMessage);</span><br><span class="line">    <span class="comment">//记录路径</span></span><br><span class="line">    gLogDir = QCoreApplication::applicationDirPath() + <span class="string">"/"</span> + logPath;</span><br><span class="line">    <span class="comment">//记录最大存储数</span></span><br><span class="line">    gLogMaxCount = logMaxCount;</span><br><span class="line">    <span class="comment">//检查存储文件夹，不存在则创建</span></span><br><span class="line">    <span class="function">QDir <span class="title">dir</span><span class="params">(gLogDir)</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (!dir.exists())</span><br><span class="line">    &#123;</span><br><span class="line">        dir.mkpath(dir.absolutePath());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//获取文件列表</span></span><br><span class="line">    QStringList infoList = dir.entryList(QDir::Files, QDir::Name);</span><br><span class="line">    <span class="comment">//硬盘空间有限，超过最大存储数的都删掉。</span></span><br><span class="line">    <span class="keyword">while</span> (infoList.size() &gt; gLogMaxCount)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//每次删第一个。文件名其实是默认按时间排序的，第一个就是时间最早的。</span></span><br><span class="line">        dir.remove(infoList.first());</span><br><span class="line">        infoList.removeFirst();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">outputMessage</span><span class="params">(QtMsgType type, <span class="keyword">const</span> QMessageLogContext &amp;context, <span class="keyword">const</span> QString &amp;msg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>log存储</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">outputMessage</span><span class="params">(QtMsgType type, <span class="keyword">const</span> QMessageLogContext &amp;context, <span class="keyword">const</span> QString &amp;msg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//每一条消息的约定格式。%1即log类型，%2即log内容。这里用静态变量，每次用的时候填充</span></span><br><span class="line">    <span class="comment">//生成一个QString副本，达到最大程度的复用。</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">const</span> QString messageTemp= QString(<span class="string">"&lt;div class=\"%1\"&gt;%2&lt;/div&gt;\r\n"</span>);</span><br><span class="line">    <span class="comment">//预定的消息类型映射表</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">char</span> typeList[] = &#123;<span class="string">'d'</span>, <span class="string">'w'</span>, <span class="string">'c'</span>, <span class="string">'f'</span>, <span class="string">'i'</span>&#125;;</span><br><span class="line">    <span class="comment">//锁</span></span><br><span class="line">    <span class="keyword">static</span> QMutex mutex;</span><br><span class="line">    <span class="comment">//取时间</span></span><br><span class="line">    QDateTime dt = QDateTime::currentDateTime();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//时间作为文件名</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//每分钟一个文件</span></span><br><span class="line">    <span class="comment">//QString fileNameDt = dt.toString("yyyy-MM-dd_hh_mm");</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//每小时一个文件</span></span><br><span class="line">    QString fileNameDt = dt.toString(<span class="string">"yyyy-MM-dd_hh"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//每天一个文件</span></span><br><span class="line">    <span class="comment">//QString fileNameDt = dt.toString("yyyy-MM-dd_");</span></span><br><span class="line">    <span class="comment">//时间戳</span></span><br><span class="line">    QString contentDt = dt.toString(<span class="string">"yyyy-MM-dd hh:mm:ss"</span>);</span><br><span class="line">    <span class="comment">//消息的前面写上时间戳，后面写内容。 msg如果是用LOG_WARN那几个宏打印的，本身已经带了文件名和行号了。</span></span><br><span class="line">    QString message = QString(<span class="string">"%1 %2"</span>).arg(contentDt).arg(msg);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//组装一条html格式的log</span></span><br><span class="line">    QString htmlMessage = messageTemp.arg(typeList[<span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(type)]).arg(message);</span><br><span class="line"></span><br><span class="line">    <span class="function">QFile <span class="title">file</span><span class="params">(QString(<span class="string">"%1/%2_log.html"</span>).arg(gLogDir).arg(fileNameDt))</span></span>;</span><br><span class="line">    <span class="comment">//这里开始锁起来，多线程安全</span></span><br><span class="line">    mutex.lock();</span><br><span class="line">    <span class="keyword">bool</span> exist = file.exists();</span><br><span class="line">    <span class="comment">//写 | 追加的方式</span></span><br><span class="line">    file.open(QIODevice::WriteOnly | QIODevice::Append);</span><br><span class="line">    <span class="comment">//文件流</span></span><br><span class="line">    <span class="function">QTextStream <span class="title">text_stream</span><span class="params">(&amp;file)</span></span>;</span><br><span class="line">    <span class="comment">//注意字符编码</span></span><br><span class="line">    text_stream.setCodec(<span class="string">"UTF-8"</span>);</span><br><span class="line">    <span class="keyword">if</span> (!exist)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//文件不存在的情况下，先把我们的html模板写进去。</span></span><br><span class="line">        text_stream &lt;&lt; logTemplate &lt;&lt; <span class="string">"\r\n"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//往文件流里面追加数据</span></span><br><span class="line">    text_stream &lt;&lt; htmlMessage;</span><br><span class="line"></span><br><span class="line">    file.close();</span><br><span class="line">    mutex.unlock();</span><br><span class="line">    <span class="comment">//解锁</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//把log都写到文件了，QtCreator 或者VS 不就看不到输出了？</span></span><br><span class="line">    <span class="comment">//这里用Win32的方式多加了一次输出，当然也可以使用std::cout fprintf。不能再使用qDebug了，因为这是在qDebug的回调里，会无限递归调用的。</span></span><br><span class="line">    ::OutputDebugString(message.toStdWString().data());</span><br><span class="line">    ::OutputDebugString(<span class="string">L"\r\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="文件句柄复用"><a href="#文件句柄复用" class="headerlink" title="文件句柄复用"></a>文件句柄复用</h2><p>有朋友(Qt侠- 刘典武)指出了优化的地方，应该复用文件句柄，不要每次都打开关闭文件，所以涛哥改了一下。</p>
<p>这里贴个小乌龟的变更图吧，当然github上也有变更记录的。</p>
<p><img src="/images/Logger/9.png" alt="预览"></p>
<h2 id="多线程测试"><a href="#多线程测试" class="headerlink" title="多线程测试"></a>多线程测试</h2><p>涛哥同时起了8个线程，每个线程输出1000条log信息，并统计最终结果。</p>
<p><img src="/images/Logger/7.png" alt="预览"></p>
<p><img src="/images/Logger/8.png" alt="预览"></p>
<p>代码去github吧。</p>
<h2 id="github仓库链接"><a href="#github仓库链接" class="headerlink" title="github仓库链接"></a>github仓库链接</h2><p><a href="https://github.com/jaredtao/TaoLogger" target="_blank" rel="noopener">TaoLogger</a></p>

    </div>
    
        <div class="reward" ontouchstart>
    <div class="reward-wrap">赏
        <div class="reward-box">
            
                <span class="reward-type">
                    <img class="alipay" src="/img/zhifubao.jpg"><b>支付宝打赏</b>
                </span>
            
            
                <span class="reward-type">
                    <img class="wechat" src="/img/weixin.jpg"><b>微信打赏</b>
                </span>
            
        </div>
    </div>
    <p class="reward-tip">赞赏是不耍流氓的鼓励</p>
</div>


    
    <div class="post-footer">
        <div>
            
                转载声明：文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a>进行许可, 转载请注明出处 © <a href="https://jaredtao.github.io" target="_blank">武威的涛哥</a>
            
        </div>
        <div>
            
        </div>
    </div>
</article>

<div class="article-nav prev-next-wrap clearfix">
    
        <a href="/2019/05/09/Qml组件化编程1-按钮的定制与封装/" class="pre-post btn btn-default" title='Qml组件化编程1-按钮的定制与封装'>
            <i class="fa fa-angle-left fa-fw"></i><span class="hidden-lg">上一篇</span>
            <span class="hidden-xs">Qml组件化编程1-按钮的定制与封装</span>
        </a>
    
    
        <a href="/2019/04/30/Qt自动化发行/" class="next-post btn btn-default" title='Qt项目持续集成系列之-github自动化发行'>
            <span class="hidden-lg">下一篇</span>
            <span class="hidden-xs">Qt项目持续集成系列之-github自动化发行</span><i class="fa fa-angle-right fa-fw"></i>
        </a>
    
</div>


    <div id="comments">
        
	
    <div id="vcomments" class="valine"></div>
    <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="/assets/valine.min.js"></script>

    <script>
        new Valine({
            av: AV,
            el: '#vcomments',
            appId: 'qegD4552u6FU9HoNDvRsonko-gzGzoHsz',
            appKey: 'eitB2FV5n2qyMHcDM7x6EQpt',
            placeholder: '说点什么吧',
            notify: false,
            verify: false,
            avatar: 'mm',
            meta: 'nick,mail'.split(','),
            pageSize: '10',
            path: window.location.pathname,
            lang: 'zh-CN'.toLowerCase()
        })
    </script>


    </div>





                </main>
                
                    <aside id="article-toc" role="navigation" class="col-md-4">
    <div class="widget">
        <h3 class="title">文章目录</h3>
        
            <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#预览"><span class="toc-text">预览</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#原理"><span class="toc-text">原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#html格式的log"><span class="toc-text">html格式的log</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Qt的log系统"><span class="toc-text">Qt的log系统</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#融合"><span class="toc-text">融合</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#文件句柄复用"><span class="toc-text">文件句柄复用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#多线程测试"><span class="toc-text">多线程测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#github仓库链接"><span class="toc-text">github仓库链接</span></a></li></ol>
        
    </div>
</aside>

                
            </div>
        </div>
    </section>
    <footer class="main-footer">
    <div class="container">
        <div class="row">
        </div>
    </div>
</footer>

<a id="back-to-top" class="icon-btn hide">
	<i class="fa fa-chevron-up"></i>
</a>




    <div class="copyright">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="busuanzi">
    
        访问量:
        <strong id="busuanzi_value_site_pv">
            <i class="fa fa-spinner fa-spin"></i>
        </strong>
        &nbsp; | &nbsp;
        访客数:
        <strong id="busuanzi_value_site_uv">
            <i class="fa fa-spinner fa-spin"></i>
        </strong>
    
</div>

            </div>
            <div class="col-sm-12">
                <span>Copyright &copy;
                    2019
                    
                </span> |
                <span>
                    Powered by <a href="//hexo.io" class="copyright-links" target="_blank" rel="nofollow">Hexo</a>
                </span> |
                <span>
                    Theme by <a href="//github.com/shenliyang/hexo-theme-snippet.git" class="copyright-links" target="_blank" rel="nofollow">Snippet</a>
                </span>
            </div>
        </div>
    </div>
</div>




    <script src="/assets/tagcanvas.min.js?rev=2.9"></script>
    <script>
        var tagOption = {
            textColour: '#444', // 字体颜色
            outlineMethod: 'block', // 选中模式
            outlineColour: '#FFDAB9', // 选中模式的颜色
            interval: 30 || 30, // 动画帧之间的时间间隔，值越大，转动幅度越大
            textHeight: 13,
            outlineRadius: 3,
            freezeActive: true || '', // 选中的标签是否继续滚动
            frontSelect: true || '', // 不选标签云后部的标签
            initial: [0.1, -0.1],
            depth: 0.5,
            decel: 0.95,
            maxSpeed: 0.03,
            reverse: true || '', // 是否反向触发
            fadeIn: 500, // 进入动画时间
            wheelZoom: false || '' // 是否启用鼠标滚轮
        }
        TagCanvas.Start('tag-cloud-3d','',tagOption);
    </script>



    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>


<script src="/js/app.js?rev=@@hash"></script>

</body>
</html>